在上一篇文章 Cypher(ㄧ) 中,我們學到了查詢資料庫,以及基本的新增 Node、Relation、Property 元素,這次來看看更多的語法。
在前一篇文章,我們設定了情境:你想找諾蘭的電影,想先確認他是否存在資料庫中?沒有的話才新增。這真的是再常見不過的需求了!難道每次都得先 MATCH 後 CREATE ? 有沒有更方便的做法?有的!MERGE
= MATCH + CREATE
MERGE (nolan:Person {name: '諾蘭'})
RETURN nolan
MERGE 會先比對這筆資料是否已存在?沒有的話才新增,否則取既有的資料即可。除了讓語法更簡潔方便,也同時解決可能會重複新增 Node 的問題。
上次最後一個範例是,新增諾蘭的電影,並指定其導演關係,如下
CREATE (nolan:Person {name: '諾蘭'}),
(m1:Movie {title: '星際效應', released: 2014}),
(m2:Movie {title: '全面啟動', released: 2010}),
(m3:Movie {title: 'TENET天能', released: 2020}),
(nolan)-[:DIRECTED]->(m1),
(nolan)-[:DIRECTED]->(m2),
(nolan)-[:DIRECTED]->(m3)
但是他的電影很多啊!上面的語法只是新增其中三個,每次都要重複寫 Movie,有什麼方法可以更簡潔的輸入呢?接下來我們介紹 UNWIND
UNWIND 可以將陣列元素轉換成一筆一筆的資料列,我們將語法修改如下
UNWIND [
{title: '追隨', released: 1998},
{title: '記憶拼圖', released: 2000},
{title: '針鋒相對', released: 2002},
{title: '蝙蝠俠:開戰時刻', released: 2005},
{title: '頂尖對決', released: 2006},
{title: '黑暗騎士', released: 2008},
{title: '全面啟動', released: 2010},
{title: '黑暗騎士:黎明昇起', released: 2012},
{title: '星際效應', released: 2014},
{title: '敦克爾克大行動', released: 2017},
{title: 'TENET天能', released: 2020}
] AS p
CREATE (m:Movie) SET m = p
RETURN m
這樣就可以新增諾蘭所有的電影
上面的語法解決了一直輸入 Movie 的問題,但沒有建立導演關係,那我們把前兩個做法合併起來試試:
MERGE (nolan:Person {name: '諾蘭'})
UNWIND [
{title: '追隨', released: 1998},
{title: '記憶拼圖', released: 2000},
{title: '針鋒相對', released: 2002},
{title: '蝙蝠俠:開戰時刻', released: 2005},
{title: '頂尖對決', released: 2006},
{title: '黑暗騎士', released: 2008},
{title: '全面啟動', released: 2010},
{title: '黑暗騎士:黎明昇起', released: 2012},
{title: '星際效應', released: 2014},
{title: '敦克爾克大行動', released: 2017},
{title: 'TENET天能', released: 2020}
] AS p
CREATE (m:Movie) SET m = p
CREATE (nolan)-[:DIRECTED]->(m)
RETURN nolan, m
WITH is required between MERGE and UNWIND (line 2, column 1 (offset: 34))
"UNWIND ["
^
會出現了如上的錯誤訊息,告訴我們 MERGE 和 UNWIND 之間必須有 WITH ,這是因為 Cypher 語言並不是 CRUD 讓你可以隨意排列組合操作的,必須有一定的順序,最常見的情境是:先查詢比對,後新增修改,這個是系統默認允許的隱性操作,不需要額外處理。
除了這樣的順序之外,其他都需要注意不能讓查詢和新增/刪除/修改同時操作,而是要視為各自獨立的區塊,用 WITH
來介接。
正確的寫法如下
MERGE (nolan:Person {name: '諾蘭'})
WITH nolan
UNWIND [
{title: '追隨', released: 1998},
{title: '記憶拼圖', released: 2000},
{title: '針鋒相對', released: 2002},
{title: '蝙蝠俠:開戰時刻', released: 2005},
{title: '頂尖對決', released: 2006},
{title: '黑暗騎士', released: 2008},
{title: '全面啟動', released: 2010},
{title: '黑暗騎士:黎明昇起', released: 2012},
{title: '星際效應', released: 2014},
{title: '敦克爾克大行動', released: 2017},
{title: 'TENET天能', released: 2020}
] AS p
CREATE (m:Movie) SET m = p
CREATE (nolan)-[:DIRECTED]->(m)
RETURN nolan, m
談到了 UNWIND
,也順便看看 FOREACH
的做法,如下
MERGE (nolan:Person {name: '諾蘭'})
WITH nolan, [
{title: '追隨', released: 1998},
{title: '記憶拼圖', released: 2000},
{title: '針鋒相對', released: 2002},
{title: '蝙蝠俠:開戰時刻', released: 2005},
{title: '頂尖對決', released: 2006},
{title: '黑暗騎士', released: 2008},
{title: '全面啟動', released: 2010},
{title: '黑暗騎士:黎明昇起', released: 2012},
{title: '星際效應', released: 2014},
{title: '敦克爾克大行動', released: 2017},
{title: 'TENET天能', released: 2020}
] AS movies
FOREACH (p in movies |
CREATE (m:Movie) SET m = p
CREATE (nolan)-[:DIRECTED]->(m)
)
RETURN nolan
FOREACH 與 UNWIND 不同的是,前者是純粹程式邏輯操作,而不是把資料展開,所以最後 RETURN 時無法回傳所有電影。
今天這一篇可能比較難懂,還是請讀者們自己也玩看看囉~ 歡迎與我討論